
// 1. 测试select
// #include <arpa/inet.h>
// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <sys/select.h>
// #include <unistd.h>
// #include <string.h>
// #include <sys/types.h>
// #include <sys/stat.h>
// #include <fcntl.h>
// #include <sys/sendfile.h>
// #include <iostream>

// int main()
// {
//     int fd = socket(AF_INET,SOCK_STREAM,0);
//     int val = 1;
//     setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val));

//     struct sockaddr_in ipv4;
//     memset(&ipv4,0,sizeof(ipv4));
//     ipv4.sin_family = AF_INET;
//     ipv4.sin_port = htons(9090);
//     ipv4.sin_addr.s_addr = inet_addr("0.0.0.0");
//     socklen_t len = sizeof(ipv4);
//     bind(fd,(const sockaddr *)&ipv4,len);

//     listen(fd,2);

//     struct sockaddr_in client;
//     len = sizeof(client);
//     //sleep(5);
//     int sockfd = accept(fd,(sockaddr *)&client,&len);

//     //1. 创建监听文件描述符集
//     fd_set read_fds;
//     fd_set warning_fds;// 监听带外事件
//     //2. 初始化描述符集
//     FD_ZERO(&read_fds);
//     FD_ZERO(&warning_fds);
//     //3. 设置监听描述符
//     // FD_SET(sockfd,&read_fds);
//     // FD_SET(sockfd,&warning_fds);
//     //4. 开始监听
//     while(true)
//     {
//         char buffer[1024] = {0};
//         // 每次事件发生都会修改里面的位图，所以需要重新放进去
//         FD_SET(sockfd,&read_fds);
//         FD_SET(sockfd,&warning_fds);
//         int n = select(sockfd+1,&read_fds,nullptr,&warning_fds,nullptr);
//         if(n == -1)
//         {
//             std::cout << "select调用失败！" << std::endl;
//             break;
//         }
//         //4.1 如果有可读事件发生
//         if(FD_ISSET(sockfd,&read_fds))
//         {
//             int ret = recv(sockfd,buffer,sizeof(buffer)-1,0);
//             if(ret > 0)
//             {
//                 buffer[ret] = 0;
//                 std::cout << "读到普通数据:" << buffer << std::endl;
//             }
//         }
//         else if(FD_ISSET(sockfd,&warning_fds))
//         {
//             int ret = recv(sockfd,buffer,sizeof(buffer)-1,MSG_OOB);
//             if(ret > 0)
//             {
//                 buffer[ret] = 0;
//                 std::cout << "读到带外数据:" << buffer << std::endl;
//             }
//         }
//     }
//     close(sockfd);
//     close(fd);
//     return 0;
// }

// 2. 测试poll
// #include <arpa/inet.h>
// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <poll.h>
// #include <unistd.h>
// #include <string.h>
// #include <sys/types.h>
// #include <sys/stat.h>
// #include <fcntl.h>
// #include <sys/sendfile.h>
// #include <iostream>

// int main()
// {
//     int fd = socket(AF_INET, SOCK_STREAM, 0);
//     int val = 1;
//     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

//     struct sockaddr_in ipv4;
//     memset(&ipv4, 0, sizeof(ipv4));
//     ipv4.sin_family = AF_INET;
//     ipv4.sin_port = htons(9090);
//     ipv4.sin_addr.s_addr = inet_addr("0.0.0.0");
//     socklen_t len = sizeof(ipv4);
//     bind(fd, (const sockaddr *)&ipv4, len);

//     listen(fd, 2);

//     struct sockaddr_in client;
//     len = sizeof(client);
//     // sleep(5);
//     int sockfd = accept(fd, (sockaddr *)&client, &len);

//     struct pollfd arr[2];

//     while (true)
//     {
//         char buffer[1024] = {0};
//         arr[0].fd = sockfd;
//         arr[0].events = POLLIN;
//         arr[1].fd = sockfd;
//         arr[1].events = POLLPRI;
//         poll(arr, sizeof(arr), -1);
//         if (arr[0].revents & POLLIN)
//         {
//             int n = recv(arr[0].fd, buffer, sizeof(buffer) - 1, 0);
//             if (n > 0)
//             {
//                 buffer[n] = 0;
//                 std::cout << "读取到数据:" << buffer << std::endl;
//             }
//         }
//         else if (arr[1].revents & POLLPRI)
//         {
//             int n = recv(arr[0].fd, buffer, sizeof(buffer) - 1, 0);
//             if (n > 0)
//             {
//                 buffer[n] = 0;
//                 std::cout << "读取到带外数据:" << buffer << std::endl;
//             }
//         }
//     }
//     close(sockfd);
//     close(fd);
//     return 0;
// }


//3. 测试epoll
// #include <arpa/inet.h>
// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <sys/epoll.h>
// #include <unistd.h>
// #include <string.h>
// #include <sys/types.h>
// #include <sys/stat.h>
// #include <fcntl.h>
// #include <sys/sendfile.h>
// #include <iostream>

// int main()
// {
//     int fd = socket(AF_INET, SOCK_STREAM, 0);
//     int val = 1;
//     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

//     struct sockaddr_in ipv4;
//     memset(&ipv4, 0, sizeof(ipv4));
//     ipv4.sin_family = AF_INET;
//     ipv4.sin_port = htons(9090);
//     ipv4.sin_addr.s_addr = inet_addr("0.0.0.0");
//     socklen_t len = sizeof(ipv4);
//     bind(fd, (const sockaddr *)&ipv4, len);

//     listen(fd, 2);

//     struct sockaddr_in client;
//     len = sizeof(client);
//     // sleep(5);
//     int sockfd = accept(fd, (sockaddr *)&client, &len);

//     int epollfd = epoll_create(1024);
//     struct epoll_event arr[2];
//     arr[0].events = EPOLLIN;
//     arr[0].data.fd = sockfd;
//     arr[1].events = EPOLLPRI;
//     arr[1].data.fd = sockfd;
//     epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,arr);

//     while(true)
//     {
//         char buffer[1024] = {0};
//         int n = epoll_wait(epollfd,arr,sizeof(arr),-1); 
//         for(int i=0;i<n;i++)
//         {
//             if(arr[i].events & EPOLLIN)
//             {
//                 int n = recv(arr[i].data.fd, buffer, sizeof(buffer) - 1, 0);
//                 if (n > 0)
//                 {
//                     buffer[n] = 0;
//                     std::cout << "读取到数据:" << buffer << std::endl;
//                 }
//             }
//             else if(arr[i].events & EPOLLPRI)
//             {
//                 int n = recv(arr[i].data.fd, buffer, sizeof(buffer) - 1, 0);
//                 if (n > 0)
//                 {
//                     buffer[n] = 0;
//                     std::cout << "读取到带外数据:" << buffer << std::endl;
//                 }
//             }
//         }
//     }
//     close(sockfd);
//     close(fd);
//     return 0;
// }



//4. 测试lt模式et模式
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/epoll.h>

#include <unistd.h>
#include <string.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <iostream>

#define EVENT_NUM 1024

void addfd(int epollfd,int fd,bool enable_et)
{
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN;
    if(enable_et)
        event.events |= EPOLLET;// et模式
    epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);// 给fd这个描述符注册一个事件
    // 所有文件都应该是非阻塞的
    int old_option = fcntl(fd,F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd,F_SETFL,new_option);
}

void lt(epoll_event *events,int num,int epollfd,int listenfd)
{
    char buffer[1024] = {0};
    for(int i=0;i<num;i++)
    {
        int socket = events[i].data.fd;
        if(socket == listenfd)
        {
            // 如果是监听套接字，说明监听事件发生了，可以获取连接了
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(socket,(sockaddr *)&client,&len);
            // 获取到连接之后，也注册一个事件，但是是lt模式
            addfd(epollfd,sockfd,false);
        }
        else if(events[i].events & EPOLLIN)
        {
            std::cout << "LT模式：事件已就绪！" << std::endl;
            // 否则就是连接套接字可读(可写)
            char buffer[1024] = {0};
            int n = recv(events[i].data.fd,buffer,sizeof(buffer)-1,0);
            if(n > 0)
            {
                std::cout << "读取到数据: " << buffer << std::endl; 
            }
            else
            {
                close(socket);
                continue;
            }
        }
    }
}

void et(epoll_event *events,int num,int epollfd,int listenfd)
{
    char buffer[1024] = {0};
    for(int i=0;i<num;i++)
    {
        int socket = events[i].data.fd;
        if(socket == listenfd)
        {
            // 如果是监听套接字，说明监听事件发生了，可以获取连接了
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(socket,(sockaddr *)&client,&len);
            // 获取到连接之后，也注册一个事件，但是是lt模式
            addfd(epollfd,sockfd,false);
        }
        else if(events[i].events & EPOLLIN)
        {
            std::cout << "ET模式：事件已就绪！" << std::endl;
            // 否则就是连接套接字可读(可写)
            while(true)
            {
                char buffer[1024] = {0};
                int n = recv(events[i].data.fd,buffer,sizeof(buffer)-1,0);
                if(n > 0)
                {
                    std::cout << "读取到数据: " << buffer << std::endl; 
                }
                else if(n < 0)
                {
                    if(errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        std::cout << "读取延迟！" << std::endl;
                        break;
                    }
                    close(socket);
                    break;
                }
                else 
                {
                    close(socket); 
                }
            }
        }
    }
}
int main()
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    int val = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

    struct sockaddr_in ipv4;
    memset(&ipv4, 0, sizeof(ipv4));
    ipv4.sin_family = AF_INET;
    ipv4.sin_port = htons(9090);
    ipv4.sin_addr.s_addr = inet_addr("0.0.0.0");
    socklen_t len = sizeof(ipv4);
    bind(fd, (const sockaddr *)&ipv4, len);

    listen(fd, 5);
    epoll_event events[EVENT_NUM];
    int epollfd = epoll_create(5);// listen监听5个连接，这里也需要创建5个事件表
    // 注册监听套接字的事件
    addfd(epollfd,fd,false);

    while(true)
    {
        int ret = epoll_wait(epollfd,events,EVENT_NUM,-1);
        if(ret < 0)
        {
            std::cout << "epoll_wait调用出错！返回值为负数！" << std::endl;
            break;
        }
        lt(events,ret,epollfd,fd);
        //et(events,ret,epollfd,fd);
    }
    
    close(fd);
    return 0;
}
